use crate::service::connection::ConnectService;
use crate::cmd_result::CmdResult;
use crate::db::{Databases, Database};
use crate::service::Connection;
use crate::utils::request::Http;
use crate::db::query::{Table, Query};
use serde::{Deserialize};

const START_WITH: &str = ",result,table,";

pub struct RealTimeDatabase { json: String }

impl RealTimeDatabase {
    pub fn json(json: String) -> Self { Self { json } }
    pub async fn find_db_by_connect(&self) -> anyhow::Result<CmdResult> {
        #[derive(Deserialize)]
        struct P { json: u16 }
        let mut s = String::new();
        
        if let Some(o) = ConnectService::find_by_id(serde_json::from_str::<P>(&self.json)?.json) {
            let dbs: Vec<Database> = Self::dbs(o).await?
                .into_iter()
                .filter(|x| x.x_type == "user")
                .collect();
            s = serde_json::to_string(&dbs)?;
        }

        CmdResult::yes("".to_string(), s)
    }
    pub async fn hello(&self) -> anyhow::Result<CmdResult> {
        let o = serde_json::from_str::<Connection>(&self.json)?;
        if Self::dbs(o).await.is_ok() {
            return CmdResult::yes(String::new(), String::new())
        }
        CmdResult::no("连接失败!".to_string())
    }
    pub async fn tables_by_db(&self) -> anyhow::Result<CmdResult> {
        #[derive(Deserialize)]
        struct P { id: u16, db: String }
        
        let p: P = serde_json::from_str(&self.json)?;
        
        if let Some(o) = ConnectService::find_by_id(p.id) {
            let s = Http::new(o.host, o.port, o.token).post("query", Query::tables(p.db)?).await?;
            let vs = Self::format(&s, Self::fm_table)?;
            return CmdResult::yes(String::new(), serde_json::to_string(&vs)?);
        }
        CmdResult::no(String::from("无效连接！"))
    }
    pub async fn generate_sdk_tags(&self) -> anyhow::Result<CmdResult> {
        #[derive(Deserialize)]
        struct P { id: u16, db: String, tb: String }
    
        let p: P = serde_json::from_str(&self.json)?;
    
        if let Some(o) = ConnectService::find_by_id(p.id) {
            let s = Http::new(o.host, o.port, o.token).post("query", Query::tags(p.db, p.tb)?).await?;
            let vs = Self::format(&s, Self::fm_generate)?;
            return CmdResult::yes(String::new(), serde_json::to_string(&vs)?);
        }
        
        CmdResult::no(String::from("无效连接！"))
    }
}

impl RealTimeDatabase {
    async fn dbs(o: Connection) -> anyhow::Result<Vec<Database>> {
        let s = Http::new(o.host, o.port, o.token).get("database").await?;
        Ok(serde_json::from_str::<Databases>(&s)?.databases)
    }
    fn fm_table(s: &str, vs: &mut Vec<Table>) {
        let ar = s.trim().split(",").collect::<Vec<&str>>();
        let len = ar.len();
        let table = ar[len - 1];
        let tag = ar[len - 3];
    
        match vs.iter().position(|z| z.name.eq(table)) {
            Some(i) => {
                if let Some(o) = vs[i].tag_names.as_mut() {
                    if !o.iter().any(|x| x.eq(tag)) {
                        o.push(tag.to_string())
                    }
                }
            },
            None => vs.push(Table {
                name: String::from(table),
                tag_names: Some(vec![String::from(tag)]),
                tags: None
            })
        }
    }
    fn fm_generate(s: &str, vs: &mut Vec<String>) {
        let ar = s.trim().split(",").collect::<Vec<&str>>();
        let len = ar.len();
        let table = ar[len - 1];
        let val = ar[len - 4];
        let field = ar[len - 2];
        let tag = ar[len - 3];
        let f_tpy = |x: &str| { // field 类型
            if x.parse::<i64>().is_ok() { return "L" }
            if x.parse::<f64>().is_ok() { return "F" }
            if x.parse::<bool>().is_ok() { return "B" }
            "S"
        };

        vs.push(format!(r#"{}_AGILOR_POINT_INFO,AGPOINTNAME={} S="{}@@{}@@{}""#,
                table, tag, tag, field, f_tpy(val)))
    }
    fn format<T>(s: &str, f: fn(&str, &mut Vec<T>)) -> anyhow::Result<Vec<T>> {
        let mut vs: Vec<T> = vec![];
        
        s.split("\n")
            .filter(|x| !x.trim().is_empty() && !x.starts_with("\n") && !x.starts_with(START_WITH))
            .for_each(|x| f(x, &mut vs));
        
        Ok(vs)
    }
}